/*
 * Copyright 2001-2014 Viewstreet Technologies www.viewstreet.com.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.viewstreet.beanspice.processor;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.Document;
import org.eclipse.text.edits.MalformedTreeException;
import org.eclipse.text.edits.TextEdit;


/**
 * The Class EditableSource. provides a container for a source document that is editable
 */
public class EditableSource {
	
	/** The file. */
	private final File file;
	
	/** The doc. */
	private final Document doc;
	
	/** The type. */
	private TypeDeclaration type;
	
	/** The compilation unit. */
	private final CompilationUnit compilationUnit;
	
	/**
	 * The Constructor.
	 *
	 * @param parser the parser
	 * @param srcPaths the src paths required to locate the SourceFile of the ITypeBinding 
	 * @param typeBinding the type binding the source is containing 
	 * @throws Exception the exception
	 */
	public EditableSource(SourceParser parser,File []srcPaths,final ITypeBinding typeBinding) throws Exception	{
		file = locateSource(srcPaths, typeBinding);
		if(!file.exists())	{
			throw new Exception("Cannot find file "+file.getAbsolutePath());
		}
		String content = SourceParser.getContents(file);		// TODO: non need to do this twice
		doc = new Document(content);
		compilationUnit = parser.runConversion(content.toCharArray(),file.getAbsolutePath(),srcPaths);
		if(typeBinding.isTopLevel())	{
			type = (TypeDeclaration)compilationUnit.types().get(0);
		}	else {
			
			compilationUnit.accept(new ASTVisitor() {
	
				@Override
				public boolean visit(TypeDeclaration node) {
					if(node.getName().toString().equals(typeBinding.getName()))	{
						type = node;
					}
					return super.visit(node);
				}
			
				
			
			});
		}
		startRecording();
	}
	
	
	/**
	 * Locate source.
	 *
	 * @param srcPaths the src paths
	 * @param typeBinding the type binding
	 * @return the file
	 */
	private File locateSource(File []srcPaths,final ITypeBinding typeBinding)	{
		File file = null;
		for (File srcRoot : srcPaths) {
			if((file=locateSource(srcRoot, typeBinding)) != null)	{
				break;
			}
		}
		return file;
	}
	
	/**
	 * Locate source.
	 *
	 * @param srcRoot the src root
	 * @param typeBinding the type binding
	 * @return the file
	 */
	private File locateSource(File srcRoot,final ITypeBinding typeBinding)	{
		File file = null;
		if(typeBinding.isTopLevel())	{
			file = new File(srcRoot,typeBinding.getQualifiedName().replace('.', File.separatorChar)+".java");
		}	else {	// ok this gets complicated we must iterate backwards down the name until we get a file hit
			
			String qualifiedName = typeBinding.getQualifiedName();
			while(file == null || !file.exists())	{
				int pos = qualifiedName.lastIndexOf('.');
				if(pos == -1)	{
					break;
				}
				qualifiedName = qualifiedName.substring(0, pos);	
				file = new File(srcRoot,qualifiedName.replace('.', File.separatorChar)+".java");
			}
		}
		return file;
	}
	
	/**
	 * Rewrite. writes the modifications back to the source file
	 *
	 * @throws MalformedTreeException the malformed tree exception
	 * @throws BadLocationException the bad location exception
	 * @throws IOException the IO exception
	 */
	public void rewrite() throws MalformedTreeException, BadLocationException, IOException	{
		TextEdit edits = compilationUnit.rewrite(doc, null);
        edits.apply(doc);
        
		FileWriter w = null;
        try
        {
            w = new FileWriter( file,false );
            w.write(doc.get());
        } finally {
        	w.close();
        }

	}
	
	
	
	/**
	 * Start recording.
	 */
	private void startRecording()	{
		compilationUnit.recordModifications();
	}
	
	/**
	 * Gets the file.
	 *
	 * @return the file
	 */
	public File getFile() {
		return file;
	}
	
	/**
	 * Gets the doc.
	 *
	 * @return the doc
	 */
	public Document getDoc() {
		return doc;
	}
	
	/**
	 * Gets the type.
	 *
	 * @return the type
	 */
	public TypeDeclaration getType() {
		return type;
	}

	/**
	 * Gets the compilation unit.
	 *
	 * @return the compilation unit
	 */
	public CompilationUnit getCompilationUnit() {
		return compilationUnit;
	}
	
	
	
}